# This file is part of Gehyra.
#
# Gehyra is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Gehyra is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Gehyra.  If not, see <http://www.gnu.org/licenses/>.

"""
@package gehyra.common.util
Useful functions to perform small common tasks
$Id: util.py 450 2011-01-20 14:28:47Z andyhhp@gmail.com $
"""

"""
@file gehyra/common/util.py
Useful functions to perform small common tasks
"""

# import struct
# import random
import sys
# import fpformat
# import re
# import array
import os
import os.path

# from twisted.python.runtime import seconds
import twisted.python.log



# # Import md5 for everyone, Python 2.4+
# # try:
#     # from hashlib import md5
# # except ImportError:
#     # from md5 import md5


# # def randbytes(n):
#     # return ''.join([chr(random.randint(0,255)) for i in range(n)])


# # def cmpify_version(ver):
#     ## Given a version string, turn it into something comparable.

#     # ver_re = re.compile("([0-9]*)(.*)$")

#     # ver_parts = []

#     # for part in ver.split('.'):
#         # m = ver_re.match(part)
#         # spart = m.group(2)
#         # try:
#             # ipart = int(m.group(1))
#         # except ValueError:
#             # ver_parts.append((spart,))
#         # else:
#             # ver_parts.append((ipart, spart))

#     # return tuple(ver_parts)


# # def hostnameMatch(hostname, regexes):
#     ## Convert a hostname into a human-readable location name.
#     # if hostname:
#         # for (r, vals) in regexes:
#             # s = re.compile(r).match(hostname)
#             # if s:
#                 # try:
#                     # return vals[s.group(1)]
#                 # except KeyError:
#                     # pass
#     # return "???"


# # def validateNick(nick):
#     # if len(nick) < 2:
#         # return "too short"

#     # if len(nick) > 30:
#         # return "too long"

#     # if not nick[0].isalpha():
#         # return "must start with a letter"

#     # chars = ("-0123456789"
#              # "ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`"
#              # "abcdefghijklmnopqrstuvwxyz{}"
#              # "!\"# %&'()*+,./:;=?@\\~")

#     # for c in nick:
#         # if c not in chars:
#             # return "contains an invalid character: '%s'" % c

#     # return ''



# # class RandSet(object):

#     # def __init__(self, init=()):
#         # self.map = {}
#         # self.set = set()
#         # for o in init:
#             # self.add(o)

#     # def __contains__(self, o):
#         # return (o in self.map)

#     # def __nonzero__(self):
#         # return bool(self.map)

#     # def __len__(self):
#         # return len(self.map)

#     # def add(self, o):
#         ## Add o to the set
#         # if o not in self.map:
#             # r = (random.random(), o)
#             # self.map[o] = r
#             # self.set.add(r)

#     # def discard(self, o):
#         ## Drop o from the set
#         # try:
#             # r = self.map.pop(o)
#         # except KeyError:
#             # return
#         # self.set.remove(r)

#     # def pop(self):
#         ## Get random element fron the set
#         # o = self.set.pop()[1]
#         # del self.map[o]
#         # return o

#     # def clear(self):
#         # self.map.clear()
#         # self.set.clear()

#     # def peek(self):
#         # o = self.pop()
#         # self.add(o)
#         # return o

def cancel_dcall(obj):
    """Cancel a Defered object.
    Checks to see if the object is active and cancels it if it is.
    @todo: this is VERY hacked up and probably terminally bad - investigate
    better solution
    """

    from twisted.internet.error import AlreadyCancelled, AlreadyCalled
    try:
        if obj and not obj.called:
            obj.cancel()
    except AlreadyCancelled:
        pass
    except AlreadyCalled:
        pass
    except AttributeError:
        pass

# # @brief Discard a Defered object
# #
# # Cancel and 'Nonify' target object
# # @param obj object
# # @param attr Defered instance
# # def dcall_discard(obj, attr):
#     # dcall = getattr(obj, attr)
#     # if dcall:
#         # dcall.cancel()
#         # setattr(obj, attr, None)


# # def dcall_timeleft(d):
#     # return max(0, d.getTime() - seconds())

    
## @brief 
def get_os():
    """Get the operating system.
    Queries sys for the platform
    @returns
     - B for BSD
     - C for Cygwin
     - L for Linux
     - M for Mac (darwin)
     - S for Sun (Solaris)
     - W for Windows
     - ? for unknown
    """
    os_map = [('bsd','B'), ('cygwin','C'), ('linux','L'),
              ('darwin','M'), ('sun','S'), ('win','W')]

    p = sys.platform.lower()

    for key, value in os_map:
        if key in p:
            return value

    return '?'


# def get_version_string():
    # import gehyra.build_config as build
    # return "Dt:%s/%s" % (build.version, get_os())

def get_user_path(filename):
    """Get the users path for data.
    Defaults to the expansion of "~/.gehyra":
     - `~/.gehyra` for *nix
     - `\%USERPROFILE%\.gehyra` for windows

     @param filename
     @return String containing filename with full path
     """
    path = os.path.expanduser("~/.gehyra")

    if path[:1] == '~':
        # Can't get a user directory, just save to cwd.
        return filename
    else:
    
        def recurse(sub_path):
            """todo remove - this is a grose abuse of the api"""
            h, t = os.path.split(sub_path)

            if t is None:
                raise OSError("Cant create suitable path")
            
            if not os.path.exists(os.path.join(h)):
                recurse(h)

            os.mkdir(os.path.join(h, t))
            
        try:
            if not os.path.exists(path):
                recurse(path)
        except OSError:
            twisted.python.log.err()

        return os.path.normpath(os.path.join(path, filename))

# # def stdlines(text):
#     # return text.replace('\r\n', '\n').replace('\r','\n')

# # def dc_escape(text):
#     # return text.replace('|','&#124;').replace('$','&# 36;')

# # def dc_unescape(text):
#     # return text.replace('&#124;','|').replace('&# 36;','$')

# def adc_escape(text):
#     return text.replace('\\', '\\\\').replace('\n','\\n').replace(' ', '\\s')

# # def adc_unescape(text):
#     # return text.replace('\\\\', '\0').replace('\\n','\n')\
#     # .replace('\\s',' ').replace('\0','\\')

# # def adc_infostring(infdict):
#     # return ' '.join(["%s%s" % (i, adc_escape(d)) for (i,d) in
#     # infdict.iteritems()])

# # def adc_locdes(infdict):
#     # if 'LO' not in infdict:
#         # return infdict

#     # loc = infdict['LO']
#     # i = loc.find('|')
#     # if i >= 0:
#         # loc = loc[:i] + loc[i+1:]

#     # infd = dict(infdict)
#     # if 'DE' in infd:
#         # infd['DE'] = "[%s] %s" % (loc, infd['DE'])
#     # else:
#         # infd['DE'] = "[%s]" % (loc)

#     # return infd

# # def adc_infodict(infstring):
#     # inf = {}
#     # for i in infstring.split(' '):
#         # inf[i[:2]] = adc_unescape(i[2:])
#     # return inf


# # def parse_gehyra_tag(info):

#     ## Break up info string
#     # try:
#         # info = split_info(info)

#     # except ValueError:
#         ## This could be an 'offline info'.
#         # if (info[:4], info[-1:]) == ('<Dt:','>'):
#             # return info[1:-1]

#     # else:
#         ## Properly formatted; extract tag
#         # desc, tag = split_tag(info[0])
#         # if tag:
#             # try:
#                 # pos = tag.rindex("Dt:")
#                 # return tag[pos:]
#             # except ValueError:
#                 # pass

#     ## Couldn't find gehyra tag
#     # return ""


# # def parse_incoming_info(info):
#     ## Pull the location and share size out of an info string
#     ## Returns dcinfo, location, shared

#     # info = info.replace('\r','').replace('\n','')

#     ## Break up info string
#     # try:
#         # info = split_info(info)
#     # except ValueError:
#         # return ("", "", 0)

#     ## Check if the location has a user-specified suffix
#     # try:
#         # location, suffix = info[2][:-1].split('|', 1)
#         # suffix = suffix[:8]
#     # except ValueError:
#         ## No separator, use entire connection field as location name
#         # location = info[2][:-1]
#     # else:
#         ## Keep location, and splice out the separator
#         # info[2] = location + suffix + info[2][-1:]

#     ## Get share size
#     # try:
#         # shared = int(info[4])
#     # except ValueError:
#         # shared = 0

#     # return ('$'.join(info), location, shared)


# # def split_tag(desc):
#     ## Break 'description<tag>' into ('description','tag')
#     # tag = ''
#     # if desc[-1:] == '>':
#         # try:
#             # pos = desc.rindex('<')
#             # tag = desc[pos+1:-1]
#             # desc = desc[:pos]
#         # except ValueError:
#             # pass
#     # return desc, tag

# # def b32pad(n):
#     # _, leftover = divmod(len(n)-1, 8)
#     # n += (7-leftover)*'='
#     # return n

# # def format_bytes(n):
#     ## Convert an integer into a Bytes representation
#     # n = float(n)
#     # suffix = ('B','KiB','MiB','GiB','TiB','PiB')
#     # i = 0
#     # while n >= 1024 and i < 5:
#         # n /= 1024
#         # i+=1

#     # if i:
#         # return "%s %s" % (fpformat.fix(n, 2), suffix[i])
#     # else:
#         # return "%d %s" % (n, suffix[i])


# # def parse_bytes(s):
#     ## Might raise ValueError

#     # mult = 1
#     # if s:
#         # s = s.strip()
#         # if s[-2:].upper() == 'IB':
#             # s = s[:-2]
#         # elif s[-1:].upper() == 'B':
#             # s = s[:-1]

#         # i = 'KMGT'.find(s[-1].upper())
#         # if i > -1:
#             # s = s[:-1]
#             # mult = 1024 ** (i+1)

#     # return int(float(s) * mult)


# # def word_wrap(line, max_len=80):

#     # lines = []

#     # words = line.split(' ')
#     # i = 0

#     # while i < len(words):
#         # cur_line = None

#         # while i < len(words):
#             # word = words[i]

#             # if cur_line is None:
#                 # cur_line = ''
#             # else:
#                 # word = ' ' + word
#                 # if len(cur_line) + len(word) > max_len:
#                     # break

#             # cur_line += word
#             # i += 1

#         # lines.append(cur_line)

#     # return lines


def CHECK(truth):
    """Checks truth.
    Runtime version of assert which wont be removed when compiling,
    and is not keyed to __debug__
    @param truth Boolean expression
    @exception AssertionError if not truth
    """
    if not truth:
        raise AssertionError("CHECK failed")

## @brief Import namespace
__all__ = [ 'get_user_path' ]
